/*
 * not type checking this file because flow doesn't play well with
 * dynamically accessing methods on Array prototype
 */

import { def } from '../util/index'

/**
 * 使用自定义的方法去覆盖原生的原型方法
 * 使用一个拦截器覆盖 Array.prototype
 * 拦截器其实就是一个和 Array.prototype 一样的 Object，里面包含的属性一模一样，
 * 只不过这个 Object 中某些可以改变数组自身内容的方法是我们经过处理的
 */
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)

const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]

/**
 * Intercept mutating methods and emit events
 */
methodsToPatch.forEach(function (method) {
  // 缓存原始方法
  const original = arrayProto[method]
  // def 函数 => Object.defineProperty
  def(arrayMethods, method, function mutator (...args) {
    const result = original.apply(this, args)
    // __ob__ 既可以访问到当前的 Observer 实例，又可以标记此属性是否是响应式的
    // this.__ob__ => value.__ob__ 
    // 因为 Array 是拦截器，而拦截器又是原型方法，所以可以直接通过 this.__ob__ 来访问 Observer 实例
    const ob = this.__ob__
    let inserted
    // 新增加的数据也需要进行侦测
    // 如果方法是 push，unshift，splice，则从 args 中将新增元素取出来，暂存在 inserted 中。
    // 接下来使用 Observer 把 inserted 的元素转换为响应式的
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    if (inserted) ob.observeArray(inserted)
    // 向依赖发送消息
    ob.dep.notify()
    return result
  })
})
